筆記目錄

Skip to content

使用 Dapper 和 ODAC Managed Driver 無法寫入 Unicode 的問題

TLDR

  • 問題情境:使用 Dapper 搭配 Oracle Managed Driver (ODAC) 寫入 Unicode 字符(如簡體中文)時,資料庫端出現亂碼。
  • 根本原因:ODP.NET 的 DbType.String 預設被對應至 OracleDbType.Varchar2,而非支援 Unicode 的 OracleDbType.NVarchar2
  • 解決方案:無法直接透過 Dapper 的 DynamicParameters 設定 OracleDbType,需實作自訂的 IDynamicParameters 類別來手動指定參數類型。

Unicode 寫入亂碼問題分析

什麼情況下會遇到這個問題:當開發者使用 Dapper 進行資料庫寫入操作,且目標欄位為 Oracle 的 NVARCHAR2NCHAR 等 Unicode 格式欄位時。

在 Oracle 的 Managed Driver 中,DbType.String 被錯誤地對應至 OracleDbType.Varchar2,這導致在處理 Unicode 字元時發生編碼轉換錯誤,進而產生亂碼。即便在 Dapper 中手動設定 DbType.String 也無法解決,因為底層驅動程式的對應邏輯並未將其導向正確的 NVarchar2 型別。

透過反組譯檢查 Oracle.ManagedDataAccess.Core (版本 3.21.100) 可發現:

  • DbType.String 對應值為 16。
  • OracleDbType.NVarchar2 對應值為 119。
  • OracleDbType.Varchar2 對應值為 126。

驅動程式內部邏輯強制將 DbType.String 映射至 OracleDbType.Varchar2,導致 Unicode 數據在寫入時遺失正確的編碼資訊。

![oracle unicode insert issue 1](../images/使用 Dapper 和 ODAC Managed Driver 無法寫入 Unicode 的問題/oracle-unicode-insert-issue-1.png) ![oracle unicode insert issue 2](../images/使用 Dapper 和 ODAC Managed Driver 無法寫入 Unicode 的問題/oracle-unicode-insert-issue-2.png)

自訂 DynamicParameters 解決方案

由於 Dapper 的 DynamicParameters 不支援直接指定 OracleDbType,開發者必須實作 SqlMapper.IDynamicParameters 介面,建立一個自訂的參數容器,以便將 OracleParameter 物件正確加入 IDbCommand 中。

以下為實作範例:

csharp
public class MyDynamicParameters : SqlMapper.IDynamicParameters {
    private readonly Dapper.DynamicParameters dynamicParameters = new();
    private readonly List<IDbDataParameter> dbDataParameters = new();

    public void Add(string name, object value, DbType? dbType, ParameterDirection? direction, int? size) {
        dynamicParameters.Add(name, value, dbType, direction, size);
    }

    public void Add(IDbDataParameter paramerter) {
        dbDataParameters.Add(paramerter);
    }

    void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity) {
        ((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);

        foreach (IDbDataParameter p in dbDataParameters) {
            command.Parameters.Add(p);
        }
    }
}

使用方式如下:

csharp
using (IDbConnection conn = new OracleConnection(connStr)) {
    conn.Open();

    MyDynamicParameters parameters = new();
    parameters.Add(new OracleParameter {
        ParameterName = "Name",
        Value = value,
        OracleDbType = OracleDbType.NVarchar2
    });
    conn.Query(sql, parameters);
}

透過此自訂類別,可以繞過 Dapper 預設的型別對應,確保參數以 OracleDbType.NVarchar2 格式傳遞至 Oracle 資料庫,從而正確處理 Unicode 字元。

異動歷程

  • 2023-06-15 初版文件建立。